/* init.c -- interfaces initialization
 *
 *    Copyright 2010 Patrick Allen
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
*/

#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>

#include "advrp.h"
#include "receiver.h"
#include "debug.h"

int32_t get_ip_info(interface * cif);
void get_broadcast(interface * cif);
interface * firstInterface = NULL;

neighbor * init_self(void)
{
	char ip[INET_ADDRSTRLEN] = "127.0.0.1";

	neighbor * self = malloc(sizeof(neighbor));	

	if (! self) {
		DBG(ERR, "Malloc failed.");
		return NULL;
	}

	/* Populate self */
	inet_pton(AF_INET, ip, &self->addr);
	strncpy(self->ipstr, ip, sizeof(self->ipstr));
	self->distance = 0;
	self->lastheard = 0;
	self->iface = get_interface(LOOPBACK_IP);
	self->firstroute = NULL;
	self->prev = NULL;
	self->next = NULL;

	return self;
}

uint32_t init_interfaces(void)
{
	interface * prevInterface, * cif;
	int ifnum, argnum;
	char ifname[IFNAMSIZ];

	DBG(DEV_INOUT, "Entered function.");

	for (ifnum = 1; if_indextoname(ifnum, ifname) != 0; ifnum++) {
		cif = malloc(sizeof(interface));
		cif->next = NULL;
		cif->ifnumber = ifnum;
		strncpy(cif->ifname, ifname,
		        sizeof(cif->ifname));
		cif->active = 0;		
		if (get_ip_info(cif) == -1) {
			free(cif);
			continue;
		}
			
		if (firstInterface == NULL) {
			firstInterface = cif;
		} else {
			prevInterface->next = cif;
		}

		prevInterface = cif;
		DBG(KERN_2, "Initialized an interface.");
	}

	/* loop through and print each interface name/number: */
	cif = firstInterface;

	while (cif != NULL) {
		DBG(MAIN_2, "Name %s is index %d.", cif->ifname, 
		    cif->ifnumber);
		cif = cif->next;
	}

	DBG(DEV_INOUT, "Leaving function.");
	return ifnum - 1;
}

int32_t get_ip_info(interface * cif)
{
	struct ifreq ifreq;
	int fd;
	char buf[IFNAMSIZ];
	struct sockaddr_in * address = (struct sockaddr_in *) &ifreq.ifr_addr;

	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
	strncpy(ifreq.ifr_name, cif->ifname, sizeof(ifreq.ifr_name));

	/* Check if this interface has broadcasting enabled */
	DBG(KERN_3, "Sending ioctls for interface: %s.", ifreq.ifr_name);
	if ((ioctl(fd, SIOCGIFFLAGS, &ifreq)) == -1) {
		DBG(ERR, "Couldn't get interface flags.");
		return -1;
	} else {
		DBG(KERN_3, "Flags received: %d.", ifreq.ifr_flags);
	}

	/* IFF_BROADCAST is in linux/if.h */
	if (ifreq.ifr_flags & IFF_BROADCAST == IFF_BROADCAST) {
		DBG(KERN_2, "Broadcast is enabled on interface %s.",
		    cif->ifname);
	} else {
		DBG(KERN_2, "Broadcast is NOT enabled on interface %s.",
		    cif->ifname);
		return -1;
	}

	/* Only attempt to get the broadcast address if broadcast
	 * is enabled on this interface. */
	if ((ioctl(fd, SIOCGIFADDR, &ifreq)) == -1) {
		DBG(KERN_2, "Couldn't get IP address.");
		return -1;
	} else {
		cif->ip = inet_addr(inet_ntoa(address->sin_addr));
		DBG(KERN_2, "IP address is: %u", cif->ip);
	}

	if ((ioctl(fd, SIOCGIFNETMASK, &ifreq)) == -1) {
		DBG(KERN_2, "Couldn't get network mask.");
		return 0;
	} else {
		cif->mask = inet_addr(inet_ntoa(address->sin_addr));
		DBG(KERN_2, "Netmask is %u.", cif->mask);
	}

	cif->network = cif->ip & cif->mask;
	DBG(KERN_2, "NetID is %u.", cif->network);
	get_broadcast(cif);
	
	return 0;
}

void get_broadcast(interface * cif)
{
	uint32_t hostmask = 0;
	uint32_t allOnes = 4294967295U; // (2^32)-1, all bits flipped on

	hostmask = cif->mask ^ allOnes;
	DBG(DEV_1, "snmask = %u, hmask = %u", cif->mask, hostmask);
	cif->broadcast = cif->network | hostmask;

	DBG(KERN_2, "Broadcast is %u.", cif->broadcast);
	return;
}
